/**
 * @file 	 mfy.c
 * @brief
 * @author 	 wenbo
 * @version  1.0
 * @date 	 2024-12-10
 *
 * @copyright Copyright (c) 2024  WENBO
 *
 */
#include "mfy.h"

#define LOGI println
#define LOGE println
#define LOGD(...) ;

#define CHECK_ERR(a, str, goto_tag, ...)                                                \
    do                                                                                  \
    {                                                                                   \
        if (!(a))                                                                       \
        {                                                                               \
            LOGE("%s(%d): " str, ##__VA_ARGS__);         		                        \
            goto goto_tag;                                                              \
        }                                                                               \
    } while (0)

#define CHECK_NULL(a, str, goto_tag, ...)   CHECK_ERR(a, str, goto_tag, ##__VA_ARGS__)
#define CHECK_NZERO(a, str, goto_tag, ...)  CHECK_ERR(0U == (a), str, goto_tag, ##__VA_ARGS__)


typedef struct {
    uint8_t 	slave_id;
    uint8_t 	port;
    uint32_t 	speed;
    uint8_t 	rec_data[64];
    uint8_t 	rec_len;
}Type_Com_Def;

struct Type_MFy_Def
{
    Type_Com_Def 	com;
    mfy_event_cb_t  event_cb;
    void* user_data;
};

// modbus 失败重试次数
#define MODBUS_TRY_TIMES        3

#define QUEUE_DEPTH             10

// 认证设备寄存器定义
/*****************************************/
// 保持寄存器
#define ADDR_HOLD_UNLOCK        0x0000
#define ADDR_HOLD_CLEAR_IRQ     0x0001
#define ADDR_HOLD_STATUS        0x0002
#define ADDR_HOLD_CARD_NUMBER   0x0003
// 线圈寄存器
#define ADDR_COIL_DO1           0x0000
#define ADDR_COIL_DO2           0x0001
#define ADDR_COIL_DO3           0x0002
#define ADDR_COIL_DO4           0x0003
#define ADDR_COIL_BEEP          0x0004
// 离散寄存器
#define ADDR_DISC_DI1           0x0000
#define ADDR_DISC_DI2           0x0001
#define ADDR_DISC_DI3           0x0002
/*****************************************/

enum State {
    STA_NONE = 0,               // 无任何异常
    STA_ELOCK_OPEN_FAULT,       // 控制开锁后电磁锁传感器仍触发(电磁锁断线，卡住，传感器故障)
    STA_ELOCK_CLOSE_FAULT,      // 控制关锁后电磁锁传感器未触发(卡住未释放到位，传感器故障)
    STA_CARD_INVALID,           // 监测到卡，但使用指定密码读取数据失败(放入了其他卡)
    STA_CARD_FAKED,             // 监测到卡，读取数据成功但解密后数据验证失败(仅复制了数据区的伪造卡)
    STA_CARD_VALID,             // 监测到卡，验证卡通过，业务逻辑中可提示下一步
    STA_PUT_OVERTIME,           // 桶放入第二凹槽超时(不满足RFID被刺破监测不到，桶传感器未触发条件超过30s)
    STA_BARRELS_ABNORMAL,       // 检测到桶传感器异常触发(在非解锁期间桶异动)
    STA_BARRELS_PUT_OK,         // 桶放入成功
    STA_MAX
};

static const char stateTip[][40] = {
    "Normal.",
    "Abnormal unlocking of Elock.",
    "Abnormal locking of Elock.",
    "Invalid card detected.",
    "Counterfeit card detected.",
    "Card authentication successful.",
    "Timeout for placing the bucket.",
    "Bucket movement detected.",
    "Bucket put success"
};

static aos_queue_t process_queue = { 0 };

static void mfy_process_thread(void* arg);

static void memcpy_resv_halfword(void* des, void* const res, uint16_t size)
{
    for (uint16_t i = 0; i < size; i += sizeof(uint16_t)) {
        *((uint8_t*)des + i + 0) = *((uint8_t*)res + i + 1);
        *((uint8_t*)des + i + 1) = *((uint8_t*)res + i + 0);
    }
}

static int xp_mfy_write_reginsters(struct Type_MFy_Def* const ctx, uint16_t addr, uint16_t* data, uint16_t reg_size)
{
    MB_data modbus;
    uint8_t try_times = MODBUS_TRY_TIMES;
    LOGD("xp_mfy_write_reginsters addr: %04X, len: %d", addr, reg_size);

    modbus.slave_id = ctx->com.slave_id;
    modbus.mb_fun = mb_hold_w;
    modbus.start_addr = addr;
    modbus.wBuf = data;
    modbus.rwLen = reg_size;
    modbus.resBuf = ctx->com.rec_data;
    modbus.resLen = &ctx->com.rec_len;
    while (try_times--) {
        if (0 == xp_modbus_ask(ctx->com.port, &modbus)) return 0;
    }

    return -1;
}

static int xp_mfy_write_coil(struct Type_MFy_Def* const ctx, uint16_t addr, bool state)
{
    uint16_t data = state ? 0xFF00 : 0x0000;
    MB_data modbus;
    uint8_t try_times = MODBUS_TRY_TIMES;
    LOGD("xp_mfy_write_coil addr: %04X, state: %d", addr, state);

    modbus.slave_id = ctx->com.slave_id;
    modbus.mb_fun = mb_coil_w;
    modbus.start_addr = addr;
    modbus.wBuf = &data;
    modbus.rwLen = 1;
    modbus.resBuf = ctx->com.rec_data;
    modbus.resLen = &ctx->com.rec_len;
    while (try_times--) {
        if (0 == xp_modbus_ask(ctx->com.port, &modbus)) return 0;
    }

    return -1;
}

static int xp_mfy_read_disc(struct Type_MFy_Def* const ctx, uint16_t addr, uint8_t* data, uint16_t byte_size)
{
    MB_data modbus;
    uint8_t try_times = MODBUS_TRY_TIMES;
    LOGD("xp_mfy_read_disc addr: %04X, byte_size: %d", addr, byte_size);

    modbus.slave_id = ctx->com.slave_id;
    modbus.mb_fun = mb_coil_r;
    modbus.start_addr = addr;
    modbus.rwLen = byte_size;
    modbus.resBuf = ctx->com.rec_data;
    modbus.resLen = &ctx->com.rec_len;
    while (try_times--) {
        if (0 == xp_modbus_ask(ctx->com.port, &modbus)) {
            memcpy(data, ctx->com.rec_data, byte_size);
            return 0;
        }
    }

    return -1;
}

static int xp_mfy_read_reginsters(struct Type_MFy_Def* const ctx, uint16_t addr, uint16_t* data, uint16_t reg_size)
{
    uint8_t resLen;
    MB_data modbus;
    uint8_t try_times = MODBUS_TRY_TIMES;
    LOGD("xp_mfy_read_reginsters addr: %04X, reg_size: %d", addr, reg_size);

    modbus.slave_id = ctx->com.slave_id;
    modbus.mb_fun = mb_hold_r;
    modbus.start_addr = addr;
    modbus.rwLen = reg_size;
    modbus.resBuf = ctx->com.rec_data;
    modbus.resLen = &ctx->com.rec_len;
    while (try_times--) {
        if (0 == xp_modbus_ask(ctx->com.port, &modbus)) {
            memcpy_resv_halfword(data, ctx->com.rec_data, reg_size * sizeof(uint16_t));
            return 0;
        }
    }
    return -1;
}

/**
 * @brief       创建认证设备 handle
 * @param[in]	port                RS485 端口
 * @param[in]	baudrate            波特率
 * @param[in]	slave_id            从机 ID
 * @param[in]	cb                  事件回调(非 NULL 则驱动器创建处理任务)
 * @return      Type_MFY_CTX        handle
 */
Type_MFY_CTX xp_mfy_creat(uint8_t port, uint32_t baudrate, uint8_t slave_id, mfy_event_cb_t cb, void* user_data)
{
    int ret = 0;
    Type_MFY_CTX ctx = aos_malloc(sizeof(struct Type_MFy_Def));
    CHECK_NULL(ctx, "mfy ctx malloc failed", err_1);

    CHECK_NZERO(xp_modbus_init(port, baudrate, MB_PAR_NONE, 100), "mfy modbus init failed", err_2);

    ctx->com.port = port;
    ctx->com.slave_id = slave_id;
    ctx->com.speed = baudrate;

    void* pQueueMem = NULL;
    if (cb && !process_queue.hdl) {
        pQueueMem = aos_malloc(sizeof(Type_MFY_CTX) * QUEUE_DEPTH);
        CHECK_NULL(pQueueMem, "pQueueMem malloc error", err_2);

        CHECK_NZERO(aos_queue_new(&process_queue, pQueueMem, sizeof(Type_MFY_CTX) * QUEUE_DEPTH, sizeof(Type_MFY_CTX)), "queue creat failed", err_3);
        CHECK_NZERO(aos_task_new("mfy_process_thread", mfy_process_thread, NULL, 2048), "creat mfy thread failed", err_4);
        LOGD("Creat MFY port: %d, baudrate: %d, slave_id: %d success, ctx: %p", port, baudrate, slave_id, ctx);
    }
    ctx->event_cb = cb;
    return ctx;
err_4:
    aos_queue_free(&process_queue);
err_3:
    aos_free(pQueueMem);
err_2:
    aos_free(ctx);
err_1:
    return NULL;
}
/**
 * @brief       销毁认证设备 handle
 * @param[in]	ctx
 */
void xp_mfy_del(Type_MFY_CTX ctx)
{
    if (ctx) {
        aos_free(ctx);
        *(&ctx) = NULL;
    }
}
/**
 * @brief       打开电磁锁,用于取出桶
 * @param[in]	ctx                 handle
 * @param[in]	elock_holdtime_ms   电磁锁打开保持时间
 * @return      bool                true: 执行成功
 */
bool xp_mfy_unlock(Type_MFY_CTX ctx, uint16_t elock_holdtime_ms)
{
    if (ctx) {
        return 0 == xp_mfy_write_reginsters(ctx, ADDR_HOLD_UNLOCK, &elock_holdtime_ms, 1);
    }
    return false;
}
/**
 * @brief       获取认证设备状态
 * @param[in]	ctx                 handle
 * @param[in]	state               状态码
 * @return      bool                true: 执行成功
 */
bool  xp_mfy_get_state(Type_MFY_CTX ctx, uint16_t* state)
{
    if (ctx && state) {
        return 0 == xp_mfy_read_reginsters(ctx, ADDR_HOLD_STATUS, state, 1);
    }
    return false;
}
/**
 * @brief       获取认证设备卡号
 * @param[in]	ctx                 handle
 * @param[in]	card_number         32位卡号
 * @return      bool                true: 执行成功
 */
bool xp_mfy_get_card_number(Type_MFY_CTX ctx, uint32_t* card_number)
{
    if (ctx && card_number) {
        return 0 == xp_mfy_read_reginsters(ctx, ADDR_HOLD_CARD_NUMBER, (uint16_t*)card_number, 2);
    }
    return false;
}
/**
 * @brief       清除认证设备发出的 IO 中断事件
 * @param[in]	ctx                 handle
 * @return      bool                true: 执行成功
 */
bool xp_mfy_clear_irq(Type_MFY_CTX ctx)
{
    if (ctx) {
        uint16_t val = 0;
        return 0 == xp_mfy_write_reginsters(ctx, ADDR_HOLD_CLEAR_IRQ, &val, 1);
    }
    return false;
}
/**
 * @brief       清除认证设备错误码
 * @param[in]	ctx                 handle
 * @return      bool                true: 执行成功
 */
bool xp_mfy_clear_error_code(Type_MFY_CTX ctx)
{
    if (ctx) {
        uint16_t val = 0;
        return 0 == xp_mfy_write_reginsters(ctx, ADDR_HOLD_STATUS, &val, 1);
    }
    return false;
}
/**
 * @brief       强制控制认证设备的输出端口
 * @param[in]	ctx                 handle
 * @param[in]	pin                 pin id (0~3 : DO1~DO4)
 * @param[in]	state               true: 打开继电器 false: 关闭继电器
 * @return      bool                true: 执行成功
 */
bool xp_mfy_froce_set_io(Type_MFY_CTX ctx, uint8_t pin, bool state)
{
    if (ctx && pin < 4) {
        uint16_t val = 0;
        return 0 == xp_mfy_write_coil(ctx, ADDR_COIL_DO1 + pin, state);
    }
    return false;
}
/**
 * @brief       读取认证设备输入端口状态
 * @param[in]	ctx                 handle
 * @param[out]	state               IO bit状态返回 (仅 bit0~bit2 有效)
 * @return      bool                true: 执行成功
 */
bool xp_mfy_get_io(Type_MFY_CTX ctx, uint8_t* state)
{
    if (ctx && state) {
        return 0 == xp_mfy_read_disc(ctx, ADDR_COIL_DO1, state, 1);
    }
    return false;
}
/**
 * @brief       IRQ 发生后触发任务处理(非阻塞)
 * @param[in]	ctx                 handle
 * @return      bool                true: 执行成功
 */
bool xp_mfy_active_irq_process(Type_MFY_CTX ctx)
{
    if (ctx && process_queue.hdl) {
        if (0 == aos_queue_send(&process_queue, &ctx, sizeof(ctx))) {
            return true;
        }
    }
    return false;
}
/**
 * @brief       获取状态字符串描述
 * @param[in]	state               认证设备状态
 * @return      const char*         状态字符串 (非法状态返回 "INVALID STATE")
 */
const char* xp_mfy_get_state_string(uint8_t state)
{
    if (state < STA_MAX) {
        return stateTip[state];
    }
    return "INVALID STATE";
}


static void mfy_process_thread(void* arg)
{
    while (1)
    {
        Type_MFY_CTX ctx;
        size_t size;
        if (0 == aos_queue_recv(&process_queue, AOS_WAIT_FOREVER, &ctx, &size)) {
            // 首先获取状态,根据状态进行下一步处理
            uint16_t state;
            LOGD("xp_mfy_get_state");
            if (!xp_mfy_get_state(ctx, &state) || state >= STA_MAX) {
                LOGE("mfy get state failed!");
                continue;
            }
            uint8_t input = 0;
            if (!xp_mfy_get_io(ctx, &input)) {
                LOGE("mfy get di state failed!");
            }
            else {
                if (ctx->event_cb) {
                    ctx->event_cb(EVT_DI_UPDATE, state, &input, ctx->user_data);
                }
            }

            // 然后尽快清除中断信号
            LOGD("xp_mfy_clear_irq");
            if (!xp_mfy_clear_irq(ctx)) {
                LOGE("mfy clear irq failed");
                // 这里清中断失败是通信问题,暂时不做处理,后续如果有必要增加通信异常错误上报
            }
            LOGI("mfy current state: %s", stateTip[state]);

            // 最后根据状态进行逻辑处理
            switch (state)
            {
            case STA_NONE:
            case STA_ELOCK_OPEN_FAULT:
            case STA_ELOCK_CLOSE_FAULT:
            case STA_CARD_INVALID:
            case STA_CARD_FAKED:
            case STA_PUT_OVERTIME:
            case STA_BARRELS_ABNORMAL:
            case STA_BARRELS_PUT_OK:
                if (ctx->event_cb) {
                    ctx->event_cb(EVT_STATE_UPDATE, state, NULL, ctx->user_data);
                }
                // LOGD("xp_mfy_clear_error_code");
                // xp_mfy_clear_error_code(ctx);
                // 所有的故障目前仅回调通知(上报至云端),后续如果有特殊逻辑在这里添加
                break;
            case STA_CARD_VALID:
            {
                uint32_t card_number;
                LOGD("xp_mfy_get_card_number");
                if (xp_mfy_get_card_number(ctx, &card_number) && ctx->event_cb) {
                    ctx->event_cb(EVT_CARD_NUMBER_REPORT, state, &card_number, ctx->user_data);
                }
            }
            break;

            default:
                break;
            }
        }
        aos_msleep(10);
    }
}